/*
 * Copyright 2012 dpassyann Corporation
 */
package cm.dpassyann.speedDating.ui.activity.update.task;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import roboguice.inject.ContextSingleton;
import roboguice.inject.InjectResource;
import android.content.Context;
import android.os.AsyncTask;
import android.provider.Settings;
import android.util.Log;

import cm.dpassyann.speedDating.R;
import cm.dpassyann.speedDating.ui.activity.update.IUpdateApplicationActivity;

import com.google.inject.Inject;

/**
 * Tâche asynchrone de mise à jour de l'application.
 * @author dpassyann
 */
@ContextSingleton
public class UpdateApplicationTask extends AsyncTask<Integer, Object, UpdateTaskResult> {

	private final IUpdateApplicationActivity	listener;
	private final Context					context;

	@InjectResource(R.string.host)
	private String							host;

	@Inject
	public UpdateApplicationTask(IUpdateApplicationActivity listener, Context context) {
		this.listener = listener;
		this.context = context;
	}

	@Override
	protected UpdateTaskResult doInBackground(Integer... params) {
		return updateApplication(params[0]);
	}

	@Override
	protected void onPostExecute(UpdateTaskResult result) {
		if (result.getException() != null) {
			listener.onUpdateFailed(result.getException());
		} else if (result.isUpToDate()) {
			listener.onApplicationUpToDate();
		} else {
			listener.onUpdateDownloaded(result.getDownloadedFile());
		}
	}

	/**
	 * Methode permettant la mise à jour de l'application
	 * @param currentVersionCode la version courante de l'application
	 * @return le fichier .apk de la nouvelle version de l'application ou rien
	 */
	private UpdateTaskResult updateApplication(int currentVersionCode) {
		try {
			int newVersionCode = retrieveNewVersionCode();

			if (newVersionCode <= currentVersionCode) {
				return UpdateTaskResult.upToDate();
			}

			int allowNonMarket = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.INSTALL_NON_MARKET_APPS, 0);
			if (allowNonMarket == 0) {
				throw new UnsupportedOperationException("Cannot install non-market apps. Please update your settings.");
			}

			String downloadedFileName = "amour-me-zam-android.apk";

			downloadUpdate(newVersionCode, downloadedFileName);

			File downloadedFile = context.getFileStreamPath(downloadedFileName);

			return UpdateTaskResult.updateAvailable(downloadedFile);

		} catch (Exception e) {
			Log.d("UpdateApplicationTask", "An exception occured while updating application", e);
			return UpdateTaskResult.exception(e);
		}
	}

	/**
	 * Méthode permettant de télécharger le fichier .apk de la nouvelle version de l'application sur la carte SD.
	 * @param newVersionCode le code de la nouvelle version de l'application disponible
	 * @param downloadedFileName le nom du fichier à télécharger(apk)
	 * @throws IOException throws when an I/O error occurs
	 */
	private void downloadUpdate(int newVersionCode, String downloadedFileName) throws IOException {
		InputStream is = getContentInputStream(host+ "/" + newVersionCode + "/" + "amour-me-zam-android.apk");

		OutputStream out = context.openFileOutput(downloadedFileName, Context.MODE_WORLD_READABLE);

		copy(is, out);

	}

	/**
	 * Méthode permettant de récupèrer le code de version de la dernière version disponible
	 * @return le code de la nouvelle version disponible de l'application
	 * @throws IOException throws when an I/O error occurs
	 */
	private int retrieveNewVersionCode() throws IOException {
		String url = host + "/AMOURMEZAM-version.txt";

		InputStream is = getContentInputStream(url);
		String versionCode = null;
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			versionCode = br.readLine();
		} finally {
			is.close();
		}
		return Integer.parseInt(versionCode);
	}

	/**
	 * Retourne un InputStream du contenu délivré par l'url fournnie en paramètre.
	 * @param url d'entrée
	 * @return contenu téléchargé (InputStream)
	 * @throws IOException throws when an I/O error occurs
	 * @throws ClientProtocolException throws when a protocol error occurs
	 */
	private InputStream getContentInputStream(String url) throws IOException, ClientProtocolException {
		HttpParams params = new BasicHttpParams();

		HttpConnectionParams.setSoTimeout(params, 3000);
		HttpConnectionParams.setConnectionTimeout(params, 3000);

		DefaultHttpClient client = new DefaultHttpClient(params);

		HttpResponse response = client.execute(new HttpGet(url));

		int statusCode = response.getStatusLine().getStatusCode();
		if (statusCode != 200) {
			//throw new RuntimeException("Response status code was not 200 but " + statusCode);
		}

		return response.getEntity().getContent();
	}

	/**
	 * Code extracted from Spring ;- Permet de copier un inputstream dans un outputstream
	 * @param in inputStream à copier - emetteur 
	 * @param out outpout sortant - recepteur
	 * @return
	 * @throws IOException
	 */
	private static int copy(InputStream in, OutputStream out) throws IOException {
		try {
			int byteCount = 0;
			byte[] buffer = new byte[1024];
			int bytesRead = -1;
			while ((bytesRead = in.read(buffer)) != -1) {
				out.write(buffer, 0, bytesRead);
				byteCount += bytesRead;
			}
			out.flush();
			return byteCount;
		} finally {
			try {
				in.close();
			} catch (IOException ex) {
			}
			try {
				out.close();
			} catch (IOException ex) {
			}
		}
	}

}
